{
 "cells": [
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-26T08:04:52.883783Z",
     "start_time": "2025-02-26T08:04:49.537788Z"
    }
   },
   "source": [
    "import matplotlib as mpl\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "import numpy as np\n",
    "import sklearn\n",
    "import pandas as pd\n",
    "import os\n",
    "import sys\n",
    "import time\n",
    "from tqdm.auto import tqdm\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "\n",
    "print(sys.version_info)\n",
    "for module in mpl, np, pd, sklearn, torch:\n",
    "    print(module.__name__, module.__version__)\n",
    "    \n",
    "device = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")\n",
    "print(device)\n"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sys.version_info(major=3, minor=12, micro=3, releaselevel='final', serial=0)\n",
      "matplotlib 3.10.0\n",
      "numpy 2.0.2\n",
      "pandas 2.2.3\n",
      "sklearn 1.6.1\n",
      "torch 2.6.0+cu126\n",
      "cuda:0\n"
     ]
    }
   ],
   "execution_count": 1
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 加载数据"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-26T08:04:57.790550Z",
     "start_time": "2025-02-26T08:04:55.700815Z"
    }
   },
   "source": [
    "from torchvision import datasets\n",
    "from torchvision.transforms import ToTensor\n",
    "\n",
    "# fashion_mnist图像分类数据集\n",
    "train_ds = datasets.FashionMNIST(\n",
    "    root=\"data\",\n",
    "    train=True,\n",
    "    download=True,\n",
    "    transform=ToTensor()\n",
    ")\n",
    "\n",
    "test_ds = datasets.FashionMNIST(\n",
    "    root=\"data\",\n",
    "    train=False,\n",
    "    download=True,\n",
    "    transform=ToTensor()\n",
    ")\n",
    "\n",
    "# torchvision 数据集里没有提供训练集和验证集的划分\n",
    "# 当然也可以用 torch.utils.data.Dataset 实现人为划分\n",
    "# 从数据集到dataloader\n",
    "train_loader = torch.utils.data.DataLoader(train_ds, batch_size=16, shuffle=True)\n",
    "val_loader = torch.utils.data.DataLoader(test_ds, batch_size=16, shuffle=False)"
   ],
   "outputs": [],
   "execution_count": 2
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-26T08:04:59.840312Z",
     "start_time": "2025-02-26T08:04:59.836759Z"
    }
   },
   "source": [
    "from torchvision.transforms import Normalize\n",
    "\n",
    "# 遍历train_ds得到每张图片，计算每个通道的均值和方差\n",
    "def cal_mean_std(ds):\n",
    "    mean = 0.\n",
    "    std = 0.\n",
    "    for img, _ in ds:\n",
    "        mean += img.mean(dim=(1, 2))\n",
    "        std += img.std(dim=(1, 2))\n",
    "    mean /= len(ds)\n",
    "    std /= len(ds)\n",
    "    return mean, std\n",
    "\n",
    "\n",
    "# print(cal_mean_std(train_ds))\n",
    "# 0.2860， 0.3205\n",
    "transforms = nn.Sequential(\n",
    "    Normalize([0.2860], [0.3205])\n",
    ")\n"
   ],
   "outputs": [],
   "execution_count": 3
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 定义模型\n",
    "\n",
    "这里我们没有用`nn.Linear`的默认初始化，而是采用了xavier均匀分布去初始化全连接层的权重\n",
    "\n",
    "xavier初始化出自论文 《Understanding the difficulty of training deep feedforward neural networks》，适用于使用`tanh`和`sigmoid`激活函数的方法。当然，我们这里的模型采用的是`relu`激活函数，采用He初始化（何凯明初始化）会更加合适。感兴趣的同学可以自己动手修改并比对效果。\n",
    "\n",
    "|神经网络层数|初始化方式|early stop at epoch| val_loss | vla_acc|\n",
    "|-|-|-|-|-|\n",
    "|20|默认|\n",
    "|20|xaviier_uniform|\n",
    "|20|he_uniform|\n",
    "|...|\n",
    "\n",
    "He初始化出自论文 《Delving deep into rectifiers: Surpassing human-level performance on ImageNet classification》"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-26T08:06:07.888675Z",
     "start_time": "2025-02-26T08:06:07.848551Z"
    }
   },
   "source": [
    "class NeuralNetwork(nn.Module):\n",
    "    def __init__(self, layers_num=2):\n",
    "        super().__init__()\n",
    "        self.transforms = transforms\n",
    "        self.flatten = nn.Flatten()\n",
    "        # 多加几层\n",
    "        self.linear_relu_stack = nn.Sequential(\n",
    "            nn.Linear(28 * 28, 100),  # in_features=784, out_features=300\n",
    "            # 一般而言，先batch norm 后 激活函数\n",
    "            # 参见论文 《Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift》\n",
    "            nn.BatchNorm1d(100),  # num of features=100\n",
    "            nn.ReLU(),\n",
    "        )\n",
    "        # 加19层\n",
    "        for i in range(1, layers_num):\n",
    "            self.linear_relu_stack.add_module(f\"Linear_{i}\", nn.Linear(100, 100))\n",
    "            self.linear_relu_stack.add_module(f\"batchnorm_{i}\", nn.BatchNorm1d(100)) #因为特征是1维的，所以用1维的batchnorm\n",
    "            self.linear_relu_stack.add_module(f\"relu\", nn.ReLU())\n",
    "        # 输出层\n",
    "        self.linear_relu_stack.add_module(\"Output Layer\", nn.Linear(100, 10))\n",
    "        \n",
    "        self.init_weights()\n",
    "        \n",
    "    def init_weights(self):\n",
    "        \"\"\"使用 xavier 均匀分布来初始化全连接层的权重 W\"\"\"\n",
    "        for m in self.modules():\n",
    "            if isinstance(m, nn.Linear):\n",
    "                nn.init.xavier_uniform_(m.weight)\n",
    "                nn.init.zeros_(m.bias)\n",
    "\n",
    "    def forward(self, x):\n",
    "        # x.shape [batch size, 1, 28, 28]\n",
    "        x = self.transforms(x)\n",
    "        x = self.flatten(x)  \n",
    "        # 展平后 x.shape [batch size, 28 * 28]\n",
    "        logits = self.linear_relu_stack(x)\n",
    "        # logits.shape [batch size, 10]\n",
    "        return logits\n",
    "\n",
    "print(f\"{'layer_name':^40}\\tparamerters num\")\n",
    "total_params = 0\n",
    "for idx, (key, value) in enumerate(NeuralNetwork(20).named_parameters()):\n",
    "    print(\"{}{:<40}\\t{:^10}\".format(idx,key, np.prod(value.shape)))\n",
    "    total_params += np.prod(value.shape)\n",
    "total_params"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "               layer_name               \tparamerters num\n",
      "0linear_relu_stack.0.weight              \t  78400   \n",
      "1linear_relu_stack.0.bias                \t   100    \n",
      "2linear_relu_stack.1.weight              \t   100    \n",
      "3linear_relu_stack.1.bias                \t   100    \n",
      "4linear_relu_stack.Linear_1.weight       \t  10000   \n",
      "5linear_relu_stack.Linear_1.bias         \t   100    \n",
      "6linear_relu_stack.batchnorm_1.weight    \t   100    \n",
      "7linear_relu_stack.batchnorm_1.bias      \t   100    \n",
      "8linear_relu_stack.Linear_2.weight       \t  10000   \n",
      "9linear_relu_stack.Linear_2.bias         \t   100    \n",
      "10linear_relu_stack.batchnorm_2.weight    \t   100    \n",
      "11linear_relu_stack.batchnorm_2.bias      \t   100    \n",
      "12linear_relu_stack.Linear_3.weight       \t  10000   \n",
      "13linear_relu_stack.Linear_3.bias         \t   100    \n",
      "14linear_relu_stack.batchnorm_3.weight    \t   100    \n",
      "15linear_relu_stack.batchnorm_3.bias      \t   100    \n",
      "16linear_relu_stack.Linear_4.weight       \t  10000   \n",
      "17linear_relu_stack.Linear_4.bias         \t   100    \n",
      "18linear_relu_stack.batchnorm_4.weight    \t   100    \n",
      "19linear_relu_stack.batchnorm_4.bias      \t   100    \n",
      "20linear_relu_stack.Linear_5.weight       \t  10000   \n",
      "21linear_relu_stack.Linear_5.bias         \t   100    \n",
      "22linear_relu_stack.batchnorm_5.weight    \t   100    \n",
      "23linear_relu_stack.batchnorm_5.bias      \t   100    \n",
      "24linear_relu_stack.Linear_6.weight       \t  10000   \n",
      "25linear_relu_stack.Linear_6.bias         \t   100    \n",
      "26linear_relu_stack.batchnorm_6.weight    \t   100    \n",
      "27linear_relu_stack.batchnorm_6.bias      \t   100    \n",
      "28linear_relu_stack.Linear_7.weight       \t  10000   \n",
      "29linear_relu_stack.Linear_7.bias         \t   100    \n",
      "30linear_relu_stack.batchnorm_7.weight    \t   100    \n",
      "31linear_relu_stack.batchnorm_7.bias      \t   100    \n",
      "32linear_relu_stack.Linear_8.weight       \t  10000   \n",
      "33linear_relu_stack.Linear_8.bias         \t   100    \n",
      "34linear_relu_stack.batchnorm_8.weight    \t   100    \n",
      "35linear_relu_stack.batchnorm_8.bias      \t   100    \n",
      "36linear_relu_stack.Linear_9.weight       \t  10000   \n",
      "37linear_relu_stack.Linear_9.bias         \t   100    \n",
      "38linear_relu_stack.batchnorm_9.weight    \t   100    \n",
      "39linear_relu_stack.batchnorm_9.bias      \t   100    \n",
      "40linear_relu_stack.Linear_10.weight      \t  10000   \n",
      "41linear_relu_stack.Linear_10.bias        \t   100    \n",
      "42linear_relu_stack.batchnorm_10.weight   \t   100    \n",
      "43linear_relu_stack.batchnorm_10.bias     \t   100    \n",
      "44linear_relu_stack.Linear_11.weight      \t  10000   \n",
      "45linear_relu_stack.Linear_11.bias        \t   100    \n",
      "46linear_relu_stack.batchnorm_11.weight   \t   100    \n",
      "47linear_relu_stack.batchnorm_11.bias     \t   100    \n",
      "48linear_relu_stack.Linear_12.weight      \t  10000   \n",
      "49linear_relu_stack.Linear_12.bias        \t   100    \n",
      "50linear_relu_stack.batchnorm_12.weight   \t   100    \n",
      "51linear_relu_stack.batchnorm_12.bias     \t   100    \n",
      "52linear_relu_stack.Linear_13.weight      \t  10000   \n",
      "53linear_relu_stack.Linear_13.bias        \t   100    \n",
      "54linear_relu_stack.batchnorm_13.weight   \t   100    \n",
      "55linear_relu_stack.batchnorm_13.bias     \t   100    \n",
      "56linear_relu_stack.Linear_14.weight      \t  10000   \n",
      "57linear_relu_stack.Linear_14.bias        \t   100    \n",
      "58linear_relu_stack.batchnorm_14.weight   \t   100    \n",
      "59linear_relu_stack.batchnorm_14.bias     \t   100    \n",
      "60linear_relu_stack.Linear_15.weight      \t  10000   \n",
      "61linear_relu_stack.Linear_15.bias        \t   100    \n",
      "62linear_relu_stack.batchnorm_15.weight   \t   100    \n",
      "63linear_relu_stack.batchnorm_15.bias     \t   100    \n",
      "64linear_relu_stack.Linear_16.weight      \t  10000   \n",
      "65linear_relu_stack.Linear_16.bias        \t   100    \n",
      "66linear_relu_stack.batchnorm_16.weight   \t   100    \n",
      "67linear_relu_stack.batchnorm_16.bias     \t   100    \n",
      "68linear_relu_stack.Linear_17.weight      \t  10000   \n",
      "69linear_relu_stack.Linear_17.bias        \t   100    \n",
      "70linear_relu_stack.batchnorm_17.weight   \t   100    \n",
      "71linear_relu_stack.batchnorm_17.bias     \t   100    \n",
      "72linear_relu_stack.Linear_18.weight      \t  10000   \n",
      "73linear_relu_stack.Linear_18.bias        \t   100    \n",
      "74linear_relu_stack.batchnorm_18.weight   \t   100    \n",
      "75linear_relu_stack.batchnorm_18.bias     \t   100    \n",
      "76linear_relu_stack.Linear_19.weight      \t  10000   \n",
      "77linear_relu_stack.Linear_19.bias        \t   100    \n",
      "78linear_relu_stack.batchnorm_19.weight   \t   100    \n",
      "79linear_relu_stack.batchnorm_19.bias     \t   100    \n",
      "80linear_relu_stack.Output Layer.weight   \t   1000   \n",
      "81linear_relu_stack.Output Layer.bias     \t    10    \n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "np.int64(275410)"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 4
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 训练"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-26T08:06:10.605852Z",
     "start_time": "2025-02-26T08:06:10.560060Z"
    }
   },
   "source": [
    "from sklearn.metrics import accuracy_score\n",
    "\n",
    "@torch.no_grad()\n",
    "def evaluating(model, dataloader, loss_fct):\n",
    "    loss_list = []\n",
    "    pred_list = []\n",
    "    label_list = []\n",
    "    for datas, labels in dataloader:\n",
    "        datas = datas.to(device)\n",
    "        labels = labels.to(device)\n",
    "        # 前向计算\n",
    "        logits = model(datas)\n",
    "        loss = loss_fct(logits, labels)         # 验证集损失\n",
    "        loss_list.append(loss.item())\n",
    "        \n",
    "        preds = logits.argmax(axis=-1)    # 验证集预测\n",
    "        pred_list.extend(preds.cpu().numpy().tolist())\n",
    "        label_list.extend(labels.cpu().numpy().tolist())\n",
    "        \n",
    "    acc = accuracy_score(label_list, pred_list)\n",
    "    return np.mean(loss_list), acc\n"
   ],
   "outputs": [],
   "execution_count": 5
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-26T08:06:14.507212Z",
     "start_time": "2025-02-26T08:06:11.347542Z"
    }
   },
   "source": [
    "from torch.utils.tensorboard import SummaryWriter\n",
    "\n",
    "\n",
    "class TensorBoardCallback:\n",
    "    def __init__(self, log_dir, flush_secs=10):\n",
    "        \"\"\"\n",
    "        Args:\n",
    "            log_dir (str): dir to write log.\n",
    "            flush_secs (int, optional): write to dsk each flush_secs seconds. Defaults to 10.\n",
    "        \"\"\"\n",
    "        self.writer = SummaryWriter(log_dir=log_dir, flush_secs=flush_secs)\n",
    "\n",
    "    def draw_model(self, model, input_shape):\n",
    "        self.writer.add_graph(model, input_to_model=torch.randn(input_shape))\n",
    "        \n",
    "    def add_loss_scalars(self, step, loss, val_loss):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/loss\", \n",
    "            tag_scalar_dict={\"loss\": loss, \"val_loss\": val_loss},\n",
    "            global_step=step,\n",
    "            )\n",
    "        \n",
    "    def add_acc_scalars(self, step, acc, val_acc):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/accuracy\",\n",
    "            tag_scalar_dict={\"accuracy\": acc, \"val_accuracy\": val_acc},\n",
    "            global_step=step,\n",
    "        )\n",
    "        \n",
    "    def add_lr_scalars(self, step, learning_rate):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/learning_rate\",\n",
    "            tag_scalar_dict={\"learning_rate\": learning_rate},\n",
    "            global_step=step,\n",
    "            \n",
    "        )\n",
    "    \n",
    "    def __call__(self, step, **kwargs):\n",
    "        # add loss\n",
    "        loss = kwargs.pop(\"loss\", None)\n",
    "        val_loss = kwargs.pop(\"val_loss\", None)\n",
    "        if loss is not None and val_loss is not None:\n",
    "            self.add_loss_scalars(step, loss, val_loss)\n",
    "        # add acc\n",
    "        acc = kwargs.pop(\"acc\", None)\n",
    "        val_acc = kwargs.pop(\"val_acc\", None)\n",
    "        if acc is not None and val_acc is not None:\n",
    "            self.add_acc_scalars(step, acc, val_acc)\n",
    "        # add lr\n",
    "        learning_rate = kwargs.pop(\"lr\", None)\n",
    "        if learning_rate is not None:\n",
    "            self.add_lr_scalars(step, learning_rate)\n"
   ],
   "outputs": [],
   "execution_count": 6
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-26T08:06:16.577831Z",
     "start_time": "2025-02-26T08:06:16.573651Z"
    }
   },
   "source": [
    "class SaveCheckpointsCallback:\n",
    "    def __init__(self, save_dir, save_step=5000, save_best_only=True):\n",
    "        \"\"\"\n",
    "        Save checkpoints each save_epoch epoch. \n",
    "        We save checkpoint by epoch in this implementation.\n",
    "        Usually, training scripts with pytorch evaluating model and save checkpoint by step.\n",
    "\n",
    "        Args:\n",
    "            save_dir (str): dir to save checkpoint\n",
    "            save_epoch (int, optional): the frequency to save checkpoint. Defaults to 1.\n",
    "            save_best_only (bool, optional): If True, only save the best model or save each model at every epoch.\n",
    "        \"\"\"\n",
    "        self.save_dir = save_dir\n",
    "        self.save_step = save_step\n",
    "        self.save_best_only = save_best_only\n",
    "        self.best_metrics = -1\n",
    "        \n",
    "        # mkdir\n",
    "        if not os.path.exists(self.save_dir):\n",
    "            os.makedirs(self.save_dir) #makedirs 创建多层目录\n",
    "        \n",
    "    def __call__(self, step, state_dict, metric=None):\n",
    "        if step % self.save_step > 0:\n",
    "            return\n",
    "        \n",
    "        if self.save_best_only:\n",
    "            assert metric is not None\n",
    "            if metric >= self.best_metrics:\n",
    "                # save checkpoints\n",
    "                torch.save(state_dict, os.path.join(self.save_dir, \"best.ckpt\"))\n",
    "                # update best metrics\n",
    "                self.best_metrics = metric\n",
    "        else:\n",
    "            torch.save(state_dict, os.path.join(self.save_dir, f\"{step}.ckpt\"))\n",
    "\n"
   ],
   "outputs": [],
   "execution_count": 7
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-26T08:06:17.213491Z",
     "start_time": "2025-02-26T08:06:17.209705Z"
    }
   },
   "source": [
    "class EarlyStopCallback:\n",
    "    def __init__(self, patience=5, min_delta=0.01):\n",
    "        \"\"\"\n",
    "\n",
    "        Args:\n",
    "            patience (int, optional): Number of epochs with no improvement after which training will be stopped.. Defaults to 5.\n",
    "            min_delta (float, optional): Minimum change in the monitored quantity to qualify as an improvement, i.e. an absolute \n",
    "                change of less than min_delta, will count as no improvement. Defaults to 0.01.\n",
    "        \"\"\"\n",
    "        self.patience = patience\n",
    "        self.min_delta = min_delta\n",
    "        self.best_metric = -1\n",
    "        self.counter = 0\n",
    "        \n",
    "    def __call__(self, metric):\n",
    "        if metric >= self.best_metric + self.min_delta:\n",
    "            # update best metric\n",
    "            self.best_metric = metric\n",
    "            # reset counter \n",
    "            self.counter = 0\n",
    "        else: \n",
    "            self.counter += 1\n",
    "            \n",
    "    @property\n",
    "    def early_stop(self):\n",
    "        return self.counter >= self.patience\n"
   ],
   "outputs": [],
   "execution_count": 8
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-26T08:06:18.025223Z",
     "start_time": "2025-02-26T08:06:18.016179Z"
    }
   },
   "source": [
    "# 训练\n",
    "def training(\n",
    "    model, \n",
    "    train_loader, \n",
    "    val_loader, \n",
    "    epoch, \n",
    "    loss_fct, \n",
    "    optimizer, \n",
    "    tensorboard_callback=None,\n",
    "    save_ckpt_callback=None,\n",
    "    early_stop_callback=None,\n",
    "    eval_step=500,\n",
    "    ):\n",
    "    record_dict = {\n",
    "        \"train\": [],\n",
    "        \"val\": []\n",
    "    }\n",
    "    \n",
    "    global_step = 0\n",
    "    model.train()\n",
    "    with tqdm(total=epoch * len(train_loader)) as pbar:\n",
    "        for epoch_id in range(epoch):\n",
    "            # training\n",
    "            for datas, labels in train_loader:\n",
    "                datas = datas.to(device)\n",
    "                labels = labels.to(device)\n",
    "                # 梯度清空\n",
    "                optimizer.zero_grad()\n",
    "                # 模型前向计算\n",
    "                logits = model(datas)\n",
    "                # 计算损失\n",
    "                loss = loss_fct(logits, labels)\n",
    "                # 梯度回传\n",
    "                loss.backward()\n",
    "                # 调整优化器，包括学习率的变动等\n",
    "                optimizer.step()\n",
    "                preds = logits.argmax(axis=-1)\n",
    "            \n",
    "                acc = accuracy_score(labels.cpu().numpy(), preds.cpu().numpy())    \n",
    "                loss = loss.cpu().item()\n",
    "                # record\n",
    "                \n",
    "                record_dict[\"train\"].append({\n",
    "                    \"loss\": loss, \"acc\": acc, \"step\": global_step\n",
    "                })\n",
    "                \n",
    "                # evaluating\n",
    "                if global_step % eval_step == 0:\n",
    "                    model.eval()\n",
    "                    val_loss, val_acc = evaluating(model, val_loader, loss_fct)\n",
    "                    record_dict[\"val\"].append({\n",
    "                        \"loss\": val_loss, \"acc\": val_acc, \"step\": global_step\n",
    "                    })\n",
    "                    model.train()\n",
    "                    \n",
    "                    # 1. 使用 tensorboard 可视化\n",
    "                    if tensorboard_callback is not None:\n",
    "                        tensorboard_callback(\n",
    "                            global_step, \n",
    "                            loss=loss, val_loss=val_loss,\n",
    "                            acc=acc, val_acc=val_acc,\n",
    "                            lr=optimizer.param_groups[0][\"lr\"],\n",
    "                            )\n",
    "                    \n",
    "                    # 2. 保存模型权重 save model checkpoint\n",
    "                    if save_ckpt_callback is not None:\n",
    "                        save_ckpt_callback(global_step, model.state_dict(), metric=val_acc)\n",
    "\n",
    "                    # 3. 早停 Early Stop\n",
    "                    if early_stop_callback is not None:\n",
    "                        early_stop_callback(val_acc)\n",
    "                        if early_stop_callback.early_stop:\n",
    "                            print(f\"Early stop at epoch {epoch_id} / global_step {global_step}\")\n",
    "                            return record_dict\n",
    "                    \n",
    "                # udate step\n",
    "                global_step += 1\n",
    "                pbar.update(1)\n",
    "                pbar.set_postfix({\"epoch\": epoch_id})\n",
    "        \n",
    "    return record_dict\n",
    "        \n",
    "\n",
    "epoch = 100\n",
    "\n",
    "# 别 20 层了 2333，太深了没法训练\n",
    "model = NeuralNetwork(layers_num=10)\n",
    "\n",
    "# 1. 定义损失函数 采用交叉熵损失\n"
   ],
   "outputs": [],
   "execution_count": 9
  },
  {
   "cell_type": "code",
   "source": [
    "loss_fct = nn.CrossEntropyLoss()"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-02-26T08:06:18.845779Z",
     "start_time": "2025-02-26T08:06:18.842594Z"
    }
   },
   "outputs": [],
   "execution_count": 10
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-26T08:06:19.719962Z",
     "start_time": "2025-02-26T08:06:19.396731Z"
    }
   },
   "cell_type": "code",
   "source": [
    "# 2. 定义优化器 采用SGD\n",
    "# Optimizers specified in the torch.optim package\n",
    "optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9)\n",
    "\n",
    "# 1. tensorboard 可视化\n",
    "tensorboard_callback = TensorBoardCallback(\"runs/bn\")\n",
    "tensorboard_callback.draw_model(model, [1, 28, 28])\n",
    "# 2. save best\n",
    "save_ckpt_callback = SaveCheckpointsCallback(\"checkpoints/bn\", save_best_only=True)\n",
    "# 3. early stop\n",
    "early_stop_callback = EarlyStopCallback(patience=10, min_delta=0.001)\n",
    "\n",
    "model = model.to(device)"
   ],
   "outputs": [],
   "execution_count": 11
  },
  {
   "cell_type": "code",
   "source": [
    "\n",
    "record = training(\n",
    "    model,\n",
    "    train_loader,\n",
    "    val_loader,\n",
    "    epoch,\n",
    "    loss_fct,\n",
    "    optimizer,\n",
    "    tensorboard_callback=tensorboard_callback,\n",
    "    save_ckpt_callback=save_ckpt_callback,\n",
    "    early_stop_callback=early_stop_callback,\n",
    "    eval_step=len(train_loader)\n",
    "    )"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-02-26T08:29:58.403625Z",
     "start_time": "2025-02-26T08:06:21.027518Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "  0%|          | 0/375000 [00:00<?, ?it/s]"
      ],
      "application/vnd.jupyter.widget-view+json": {
       "version_major": 2,
       "version_minor": 0,
       "model_id": "9196a6a3dece4037a2f0c9d502f505d0"
      }
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Early stop at epoch 46 / global_step 172500\n"
     ]
    }
   ],
   "execution_count": 12
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-26T08:30:26.671565Z",
     "start_time": "2025-02-26T08:30:26.428843Z"
    }
   },
   "source": [
    "#画线要注意的是损失是不一定在零到1之间的\n",
    "def plot_learning_curves(record_dict, sample_step=500):\n",
    "    # build DataFrame\n",
    "    train_df = pd.DataFrame(record_dict[\"train\"]).set_index(\"step\").iloc[::sample_step]\n",
    "    val_df = pd.DataFrame(record_dict[\"val\"]).set_index(\"step\")\n",
    "\n",
    "    # plot\n",
    "    fig_num = len(train_df.columns)\n",
    "    fig, axs = plt.subplots(1, fig_num, figsize=(6 * fig_num, 5))\n",
    "    for idx, item in enumerate(train_df.columns):    \n",
    "        axs[idx].plot(train_df.index, train_df[item], label=f\"train_{item}\")\n",
    "        axs[idx].plot(val_df.index, val_df[item], label=f\"val_{item}\")\n",
    "        axs[idx].grid()\n",
    "        axs[idx].legend()\n",
    "        axs[idx].set_xlabel(\"step\")\n",
    "    \n",
    "    plt.show()\n",
    "\n",
    "plot_learning_curves(record, sample_step=10000)  #横坐标是 steps"
   ],
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<Figure size 1200x500 with 2 Axes>"
      ],
      "image/png": "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"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "execution_count": 13
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-26T08:30:33.871346Z",
     "start_time": "2025-02-26T08:30:31.402763Z"
    }
   },
   "source": [
    "# dataload for evaluating\n",
    "model=model.to(device)\n",
    "# load checkpoints\n",
    "model.load_state_dict(torch.load(\"checkpoints/bn/best.ckpt\",weights_only=True, map_location=\"cpu\"))\n",
    "\n",
    "model.eval()\n",
    "loss, acc = evaluating(model, val_loader, loss_fct)\n",
    "print(f\"loss:     {loss:.4f}\\naccuracy: {acc:.4f}\")"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "loss:     0.3035\n",
      "accuracy: 0.8926\n"
     ]
    }
   ],
   "execution_count": 14
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": ""
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
